/**
 \file tp5.cpp

 \brief Programme d'aide au pilote.

 \author   Clement Zotti 11074801
 \author   Ousmane Iria 10100204
 \date     22/03/2012
 \version  v1.0

 <b>Entrees:</b>
 \li (\c clavier) coordonnées du pilote  (\c chaine de caractères)
 \li (\c clavier) action à effectue  (\c chaine de caractères)
 \li (\c fichier) Fichier de coordonnées  (\c chaines de caractères)

 <b>Sorties:</b>
 \li (\c écran) liste des aéroports ainsi que de leurs coordonnées (\c chaine de caractères)

 Ce programme aide le pilote pour le choix d'un aéroport, selon
 la méthode de tri choisie. Dans un premier temps, le pilote
 entre ses coordonnées GPS dans le programme. Ensuite il choisie une action
 entre ("alpha.", "distance.", "suivant.", "fin."), en fonction de ce choix
 soit le programme tri en fonction des coordonnées, soit en fonction des
 distance le séparant des aéroports. Ou il peut a nouveau rentrer ses
 nouvelles coordonnées et enfin il peut aussi mettre fin au programme.

 Date de la premiere version: 22/03/2012 \n

 Date de modification:
 \li 22/03/2012, modification des fonctions et ajout des commentaires, Clement Zotti
 */

/// defini la valeur de l'action ajouter une image
#define VAL_AJOUT 1
/// defini la valeur de l'action afficher une image
#define VAL_AFF 2
/// defini la valeur de l'action retirer une image
#define VAL_RET 3
/// defini la valeur de l'action fin du programme
#define VAL_FIN 4
/// defini la valeur de l'action non pour le tri
#define VAL_NON 11
/// defini la valeur de l'action oui pour le tri
#define VAL_OUI 10
/// defini la valeur de l'action moyenne d'une image
#define VAL_MOY 20
/// defini la valeur de l'action variance d'une image
#define VAL_VAR 21
/// defini la valeur de l'action kurtosis d'une image
#define VAL_KURT 22
/// defini la valeur de l'action skewness d'une image
#define VAL_SKEW 23
/// defini la valeur de l'action ressemblance d'une image
#define VAL_RESS 24

/*-- Bibliotheques utilisees en ordre alphabetique --*/

#include <cmath>
#include <vector>
#include <fstream>
#include <iostream>
#include <string>


using namespace std;


struct date_t
{
	int jour;
	string mois;
	int annees;
};

/** \struct
  \brief structure contenant les informations d'une image

  Cet enregistrement et fait pour gerer une image en ton de gris 8bits.
**/
struct image_8_bits_t
{
    // Déclaration des constantes
    static const int l_x = 40000;

    // Déclarations des variables
    string identificateur;
    date_t date;
    string nom_fichier;
    unsigned char buff_pixel[l_x];
    int longueur;
    double moyenne;
    double variance;
    double sigma;
    double skewness;
    double kurtosis;
    double ressemblance;

    void init()
    {
        identificateur = "";
        nom_fichier = "";
        moyenne = 128;
        variance = 0;
        skewness = 0;
        kurtosis = 0;
        sigma = 0;
    }
};


/**
 \brief Fonction main qui est utilisé seulement pour appeler notre programme
 */

int main(void) {
	// Déclaration des focntions;
	void manipulation_image();

	// Boucle principale

	manipulation_image();

	// retour
	return 0;
}

/**
 \brief Fonction pricipale du programme du traitement d'image.

 Cette fonction manipule les images contenus dans la liste

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 **/
void manipulation_image() {
	//Déclaration des constantes

	//Déclaration des fonctions
	int ajout_image(vector<image_8_bits_t>&);
	int retirer_image(vector<image_8_bits_t>&);
	int affiche_image(vector<image_8_bits_t>&);
	double calcul_ressemblance(image_8_bits_t &, image_8_bits_t &);
	int lecture_action();

	// Déclaration des variables
	vector<image_8_bits_t> liste_image;
	int action = 0;
	string index1, index2;

	// Corps de la fonction
	do
	{
		cout << "Entrez votre action (ajouter, retirer, afficher)" << endl;
		action = lecture_action();
		if(action == VAL_AJOUT)
			ajout_image(liste_image);
		else if (action == VAL_AFF)
			affiche_image(liste_image);
		else if (action == VAL_RET)
			retirer_image(liste_image);
	} while (action != VAL_FIN);
}

/**
 \brief Lecture d'une action au clavier

 Cette fonction lit une action au clavier jusqu'à qu'elle soit bonne.

 Cette fonction est correcte.
 Cette fonction est robuste.

  \return Valeur de l'action choisie
 **/
int lecture_action()
{
	// Déclaration des constantes
	const string AJOUT = "ajouter";
	const string AFF = "afficher";
	const string RET = "retirer";
	const string FIN = "fin";
	const string NON = "non";
	const string OUI = "oui";
	const string MOY = "moyenne";
	const string VAR = "variance";
	const string KURT = "kurtosis";
	const string SKEW = "skewness";
	const string RESS = "ressemblance";

	// Declaration des fonctions utilisees dans la fonction

	// Declaration des variables locales
	string action = "";
	int val = -1;

	// Corps de la fonction
	do
	{
		cin >> action;
		if (action == AJOUT)
		{
			val = VAL_AJOUT;
		}
		else if (action == AFF)
		{
			val = VAL_AFF;
		}
		else if (action == RET)
		{
			val = VAL_RET;
		}
		else if (action == FIN)
		{
			val = VAL_FIN;
		}
		else if (action == NON)
		{
			val = VAL_NON;
		}
		else if (action == OUI)
		{
			val = VAL_OUI;
		}
		else if (action == MOY)
		{
			val = VAL_MOY;
		}
		else if (action == VAR)
		{
			val = VAL_VAR;
		}
		else if (action == KURT)
		{
			val = VAL_KURT;
		}
		else if (action == SKEW)
		{
			val = VAL_SKEW;
		}
		else if (action == RESS)
		{
			val = VAL_RESS;
		}
	} while (val == -1);

	return val;
	// ne pas oublier le retour
}


/**
 \brief Retirer une image

 Cette fonction retire une image du vecteur d'enregistrement,
 apres avoir fait un tri préliminaire.

 Cette fonction est correcte.
 Cette fonction est robuste.

 \param[in,out] image (Vecteur d'image)

 \return le nombre d'aéroports présent dans le fichier.
 **/
int retirer_image(vector<image_8_bits_t> &liste_image)
{
	// Declaration des fonctions utilisees dans la fonction
	int lecture_action();
	int affiche_image(vector<image_8_bits_t>&);

	// Declaration des variables locales
	int index_retrait = 0;

	// Corps de la fonction
	affiche_image(liste_image);
	cout << "Quel index d'image voulez-vous enlever ?" << endl;
	cin >> index_retrait;

	liste_image.erase(liste_image.begin() + index_retrait);

	return 0;
	// ne pas oublier le retour
}

/**
 \brief Affiche la liste d'image

 Cette fonction affiche les informations des images du vecteur.

 Cette fonction est correcte.
 Cette fonction est robuste.

 \param[in] image (Vecteur d'image)

 \return le nombre d'aéroports présent dans le fichier.
 **/
int affiche_image(vector<image_8_bits_t> &liste_image)
{
	// Déclaration des fonctions
	double calcul_ressemblance(image_8_bits_t &, image_8_bits_t &);
	int tri_fusion(int, vector<image_8_bits_t>&);
	int lecture_action();

	// Déclaration des constantes
	const float COR_PAR_POS = 0.98;
	const float COR_PAR_NEG = -0.98;
	const float COR_FOR_POS = 0.80;
	const float COR_FOR_NEG = -0.80;
	const float COR_MOY_POS = 0.60;
	const float COR_MOY_NEG = -0.60;

	// Déclaration variables
	int action;
	string id;
	bool id_trouve = false;
	int index_r = 0;

	// Corps de la fonction
	cout << "Besoin de trier (oui/non) : " << endl;
	action = lecture_action();

	if(action == VAL_OUI)
	{
		cout << "Quel type de tri (moyenne, kurtosis) :" << endl;
		action = lecture_action();
		if(action == VAL_RESS)
		{
			do
			{
				cout << "Identificateur de l'image de comparaison : " << endl;
				cin >> id;
				int i = 0;
				while(i < (int)liste_image.size())
				{
					if(liste_image.at(i).identificateur == id)
					{
						id_trouve = true;
						index_r = i;
					}
					i++;
				}
			}while(!id_trouve);

			for(int i = 0 ; i < (int)liste_image.size() ; i ++)
				liste_image.at(i).ressemblance = calcul_ressemblance(liste_image.at(index_r), liste_image.at(i));
		}
		if(action == VAL_RESS || action == VAL_KURT ||
		   action == VAL_KURT || action == VAL_SKEW ||
		   action == VAL_VAR || action == VAL_MOY)
			tri_fusion(action, liste_image);
	}

	for (int i = 0; i < (int)liste_image.size(); i++)
	{
		cout << "Numero : " << i << endl;
		cout << "Date : " << liste_image.at(i).date.jour << " " << liste_image.at(i).date.mois << " " << liste_image.at(i).date.annees << endl
				<< "ID : " << liste_image.at(i).identificateur << endl
				<< "Moyenne = " << liste_image.at(i).moyenne << endl
				<< "Variance = " << liste_image.at(i).variance << endl
				<< "Sigma = " << liste_image.at(i).sigma << endl
				<< "Skewness = " << liste_image.at(i).skewness << endl
				<< "Kurtosis = " << liste_image.at(i).kurtosis << endl;

		if(liste_image.at(i).ressemblance > COR_PAR_POS || liste_image.at(i).ressemblance < COR_PAR_NEG)
			cout << "Corrélation parfaite, " << liste_image.at(i).ressemblance << endl << endl;
		else if(liste_image.at(i).ressemblance > COR_FOR_POS || liste_image.at(i).ressemblance < COR_FOR_NEG)
			cout << "Corrélation forte, " << liste_image.at(i).ressemblance << endl << endl;
		else if(liste_image.at(i).ressemblance > COR_MOY_POS || liste_image.at(i).ressemblance < COR_MOY_NEG)
			cout << "Corrélation moyenne, " << liste_image.at(i).ressemblance << endl << endl;
		else
			cout << "Corrélation faible, " << liste_image.at(i).ressemblance << endl << endl;

	}

	return 0;
	// ne pas oublier le retour
}

/**
 \brief Ajout d'une image.

 Cette fonction ajoute une image à la liste d'image

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in,out] liste_image (Vecteur d'image)

 \return entier.
 **/
int ajout_image(vector<image_8_bits_t> &liste_image)
{
	// Declaration des fonctions utilisees dans la fonction
	void calcul_moyenne(image_8_bits_t &);
	void calcul_variance(image_8_bits_t &);
	void calcul_skewness(image_8_bits_t &);
	void calcul_kurtosis(image_8_bits_t &);

	// Declaration des variables locales
	bool fOK = false;
	int ajout = 0;
	image_8_bits_t image_tmp;

	ifstream fImage;

	// Corps de la fonction
	do
	{
		cout << "Entrez le nom du fichier de l'image :";
		cin >> image_tmp.nom_fichier;
		fImage.open(image_tmp.nom_fichier.c_str(), ios::in);
		if (!fImage)
		{
			fImage.clear();
			cout << "Erreur a l'ouverture." << endl;
		} else
			fOK = true;
	} while (!fOK);

	{
		int i = 0;
		while( i < image_tmp.l_x && !fImage.eof())
		{
			image_tmp.buff_pixel[i] = fImage.get();
			i++;
		}
		if(i <= image_tmp.l_x)
			image_tmp.longueur = i;

	}
	fImage.close();
	//Fin lecture du fichier

	cout << "Identificateur de l'image" << endl;
	cin >> image_tmp.identificateur;

	cout << "Date de l'image (jj mois aaaa) :" << endl;
	cin >> image_tmp.date.jour >> image_tmp.date.mois >> image_tmp.date.annees;

	calcul_moyenne(image_tmp);
	calcul_variance(image_tmp);
	calcul_skewness(image_tmp);
	calcul_kurtosis(image_tmp);

	liste_image.push_back(image_tmp);

	return ajout;
	// ne pas oublier le retour
}

/**
 \brief Calcul la moyenne

 Fonction qui calcule la moyenne des pixels de l'image.

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in] image (Structure image)

 **/
void calcul_moyenne(image_8_bits_t & image)
{
	for(int j = 1; j<= image.l_x; j++)
    {
		image.moyenne += image.buff_pixel[j];
    }
    image.moyenne /= (image.l_x);

#ifdef _DEBUG
    cout << "moyenne = " << image.moyenne << endl;
#endif
}

/**
 \brief Calcul de la variance

 Fonction de calcul de la variance d'une image

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in] image (Structure image)

 **/
void calcul_variance(image_8_bits_t & image)
{
	for (int i = 0; i < image.l_x; i++)
	{
			image.variance += pow(((int) image.buff_pixel[i] - image.moyenne), 2);
	}
	image.variance /= (image.l_x * 1.0);

#ifdef _DEBUG
	cout << "Variance=" << image.variance << endl;
#endif
}

/**
 \brief Calcul du skewness

 Fonction de calcul du skewness d'une image

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in] image (Structure image)
**/
void calcul_skewness(image_8_bits_t & image)
{
	image.sigma = sqrt(image.variance);
	for (int i = 0; i < image.l_x; i++)
	{
			image.skewness += pow(((int) image.buff_pixel[i] - image.moyenne) / image.sigma , 3);
	}
	image.skewness /= (image.l_x * 1.0);

#ifdef _DEBUG
	cout << "Sigma=" << image.sigma << endl;
	cout << "Skewness=" << image.skewness << endl;
#endif
}

/**
 \brief Calcul du kurtosis

 Fonction de calcul du kurtosis d'une image

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in] image (Structure image)
**/
void calcul_kurtosis(image_8_bits_t & image)
{
	for(int i = 1 ; i < image.l_x; i++)
	{
		image.kurtosis += pow((((int)image.buff_pixel[i]-image.moyenne) / image.sigma), 4);
	}
    image.kurtosis /= (image.l_x);
    image.kurtosis -= 3;

#ifdef _DEBUG
    cout << "kurtosis" << image.kurtosis << endl;
#endif
}

/**
 \brief Calcul de la ressemblance entre 2 images.

 Fonction de calcul de la ressemblance entre 2 image

 Cette fonction est correcte.
 Cette fonction n'est pas robuste.

 \param[in] image_comparant (Structure image)
 \param[in] image_compare (Structure image)

**/
double calcul_ressemblance(image_8_bits_t & image_comparant, image_8_bits_t & image_compare)
{
	// Déclaration des constantes
	// Déclaration des fonctions
	// Déclaration des variables
	double r = -1;
	/// produit, somme des produits des pixels des deux images
	/// x_c, somme des carrés de l'image comparant
	/// y_c, somme des carrés de l'image comparé
	double produit, x_c, y_c;

	// Boucle principale
	produit = x_c = y_c = 0.0;

	for(int i = 0 ; i < image_comparant.l_x ; i++)
	{
		produit += (image_comparant.buff_pixel[i] - image_comparant.moyenne) *
					(image_compare.buff_pixel[i] - image_compare.moyenne) * 1.0;

		x_c += pow((image_comparant.buff_pixel[i] - image_comparant.moyenne),2);

		y_c += pow((image_compare.buff_pixel[i] - image_compare.moyenne),2);
	}
	r = produit / (sqrt(x_c) * sqrt(y_c));

	return r;
}



int tri_fusion(int action ,vector<image_8_bits_t> &liste_image)
{
    vector<image_8_bits_t> liste1, liste2;
    int longueur1, longueur2;
    int index, index1, index2;

    // traitement des cas d’arret
    if ((int)liste_image.size() > 1)
    {
		// decoupage de la liste
		longueur1 = (int)liste_image.size() / 2;
		longueur2 = (int)liste_image.size() - longueur1;
		for(int i = 0; i < longueur1; i++)
		{
			liste1.push_back(liste_image.at(i));
		}
		for (int i = 0; i < longueur2; i++)
		{
			liste2.push_back(liste_image.at(longueur1+i));
		}

		// tri des deux sous-listes
		tri_fusion(action, liste1);
		tri_fusion(action, liste2);

		// fusion des deux sous-listes triees
		index1 = 0;
		index2 = 0;

		// Comparaison jusqu’a epuisement d’une liste
		switch(action)
		{
			case VAL_MOY :
				for(index=0; index1<longueur1 && index2<longueur2; index+=1)
				{
					if (liste1.at(index1).moyenne < liste2.at(index2).moyenne)
					{
						liste_image.at(index) = liste1.at(index1);
						index1 += 1;
					}
					else
					{
						liste_image.at(index) = liste2.at(index2);
						index2 += 1;
					}
				}
				break;
			case VAL_KURT :
				for(index=0; index1<longueur1 && index2<longueur2; index+=1)
				{
					if (liste1.at(index1).kurtosis < liste2.at(index2).kurtosis)
					{
						liste_image.at(index) = liste1.at(index1);
						index1 += 1;
					}
					else
					{
						liste_image.at(index) = liste2.at(index2);
						index2 += 1;
					}
				}
				break;
			case VAL_SKEW :
				for(index=0; index1<longueur1 && index2<longueur2; index+=1)
				{
					if (liste1.at(index1).skewness < liste2.at(index2).skewness)
					{
						liste_image.at(index) = liste1.at(index1);
						index1 += 1;
					}
					else
					{
						liste_image.at(index) = liste2.at(index2);
						index2 += 1;
					}
				}
				break;
			case VAL_VAR :
				for(index=0; index1<longueur1 && index2<longueur2; index+=1)
				{
					if (liste1.at(index1).variance < liste2.at(index2).variance)
					{
						liste_image.at(index) = liste1.at(index1);
						index1 += 1;
					}
					else
					{
						liste_image.at(index) = liste2.at(index2);
						index2 += 1;
					}
				}
				break;
			case VAL_RESS :
				for(index=0; index1<longueur1 && index2<longueur2; index+=1)
				{
					if (liste1.at(index1).ressemblance < liste2.at(index2).ressemblance)
					{
						liste_image.at(index) = liste1.at(index1);
						index1 += 1;
					}
					else
					{
						liste_image.at(index) = liste2.at(index2);
						index2 += 1;
					}
				}
				break;
		}

		// completion par la liste non vide
		while (index1 < longueur1)
		{
			liste_image.at(index) = liste1.at(index1);
			index += 1;
			index1 += 1;
		}
		while (index2 < longueur2)
		{
			liste_image.at(index) = liste2.at(index2);
			index += 1;
			index2 += 1;
		}
    }
    return 0;
}









